﻿// -----------------------------------------------------------------------
// <copyright file="JPGImage.cs" company="Christoph van der Fecht">
// This code can be used in Comercial, free and opensource projects
// </copyright>
// -----------------------------------------------------------------------

namespace MediaTool.Model.Picture
{
    using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;

    /// <summary>
    /// Represents a jpg image with all needed properties.
    /// </summary>
    public class JPGImage : ImageBase, IComparable
    {
        /// <summary>
        /// Name of the <see cref="PicturePath"/> property.
        /// </summary>
        private const string PicturePropertyName = "PicturePath";

        /// <summary>
        /// Name of the <see cref="Filename"/> property.
        /// </summary>
        private const string FilenamePropertyName = "Filename";

        /// <summary>
        /// Name of the <see cref="Longitude"/> property.
        /// </summary>
        private const string LongitudeName = "Longitude";

        /// <summary>
        /// Name of the <see cref="Latitude"/> property.
        /// </summary>
        private const string LatitudeName = "Latitude";

        /// <summary>
        /// Longitude the picture was taken.
        /// </summary>
        private float longitude = 0.0f;

        /// <summary>
        /// Latitude the picture was taken.
        /// </summary>
        private float latitude = 0.0f;

        /// <summary>
        /// Initializes a new isntance of the <see cref="JPGImage"/> class, using the provided picture.
        /// </summary>
        /// <param name="picture"><see cref="ImageBase"/> that contains the jpg file.</param>
        public JPGImage(string path)
        {
            this.PicturePath = path;
            ////this.DateTaken = this.ReadDateTakenTimestamp();
        }

        /// <summary>
        /// Gets or sets the picture path.
        /// </summary>
        public string PicturePath
        {
            get
            {
                return this.picturePath;
            }

            set
            {
                if (value == this.picturePath)
                {
                    return;
                }

                this.picturePath = value;
                this.RaisePropertyChanged(PicturePropertyName);
                this.UpdateFilename();
            }
        }

        /// <summary>
        /// Gets the file name of the picture.
        /// </summary>
        public string Filename
        {
            get
            {
                return this.filename;
            }

            private set
            {
                if (value == this.filename)
                {
                    return;
                }

                this.filename = value;
                this.RaisePropertyChanged(FilenamePropertyName);
            }
        }

        /// <summary>
        /// Gets the longitude the picture was taken.
        /// </summary>
        public float Longitude
        {
            get
            {
                return this.longitude;
            }

            private set
            {
                if (value == this.longitude)
                {
                    return;
                }

                this.longitude = value;
                this.RaisePropertyChanged(LongitudeName);
            }
        }

        /// <summary>
        /// Gets the latitude the picture was taken.
        /// </summary>
        public float Latitude
        {
            get
            {
                return this.latitude;
            }

            private set
            {
                if (value == this.latitude)
                {
                    return;
                }

                this.latitude = value;
                this.RaisePropertyChanged(LatitudeName);
            }
        }

        /// <summary>
        /// Compares the date taken timestamp of the provided picture with the curren one.
        /// </summary>
        /// <param name="obj">Picture to compare.</param>
        /// <returns>
        /// -1 if the provided timestamp is greater then this.
        /// 1 if it is smaller
        /// 0 if they are equal.
        /// </returns>
        public int CompareTo(object obj)
        {
            JPGImage current = null;
            if (obj is JPGImage)
            {
                current = obj as JPGImage;
            }
            else
            {
                return -1;
            }

            if (current == null)
            {
                return -1;
            }

            if (current.DateTaken > this.DateTaken)
            {
                return -1;
            }
            else if (current.DateTaken < this.DateTaken)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }

        /// <summary>
        /// Read the timestamp the picture was taken and store it in the <see cref="DateTaken"/> property.
        /// </summary>
        public void ReadDateTaken()
        {
            this.DateTaken = this.ReadDateTakenTimestamp();
        }

        /// <summary>
        /// Reads the geotag of the picture and stores the data into <see cref="Longitude"/> and <see cref="Latitude"/>.
        /// </summary>
        public void ReadGeoTags()
        {
            if (string.IsNullOrEmpty(this.picturePath))
            {
                return;
            }

            Image picture = Image.FromFile(this.PicturePath);

            // get the property at id 0x0002
            PropertyItem latitudeItem = picture.GetPropertyItem(0x0002);

            // get the property at id 0x0004
            PropertyItem longitudeItem = picture.GetPropertyItem(0x0004);

            picture.Dispose();

            uint degreesNumerator = 0;
            uint degreesDenominator = 0;
            uint minutesNumerator = 0;
            uint minutesDenominator = 0;
            uint secondsNumerator = 0;
            uint secondsDenominator = 0;

            // process latitude
            degreesNumerator = BitConverter.ToUInt32(latitudeItem.Value, 0);
            degreesDenominator = BitConverter.ToUInt32(latitudeItem.Value, 4);

            minutesNumerator = BitConverter.ToUInt32(latitudeItem.Value, 8);
            minutesDenominator = BitConverter.ToUInt32(latitudeItem.Value, 12);

            secondsNumerator = BitConverter.ToUInt32(latitudeItem.Value, 16);
            secondsDenominator = BitConverter.ToUInt32(latitudeItem.Value, 20);

            string latitudeString = string.Format("{0},{1},{2}",
                ((float)(degreesNumerator / degreesDenominator)).ToString(),
                ((float)(minutesNumerator / minutesDenominator)).ToString(),
                ((float)(secondsNumerator / secondsDenominator)).ToString()
                );

            // process longitude
            degreesNumerator = BitConverter.ToUInt32(longitudeItem.Value, 0);
            degreesDenominator = BitConverter.ToUInt32(longitudeItem.Value, 4);

            minutesNumerator = BitConverter.ToUInt32(longitudeItem.Value, 8);
            minutesDenominator = BitConverter.ToUInt32(longitudeItem.Value, 12);

            secondsNumerator = BitConverter.ToUInt32(longitudeItem.Value, 16);
            secondsDenominator = BitConverter.ToUInt32(longitudeItem.Value, 20);

            string longitudeString = string.Format("{0},{1},{2}",
                ((float)(degreesNumerator / degreesDenominator)).ToString(),
                ((float)(minutesNumerator / minutesDenominator)).ToString(),
                ((float)(secondsNumerator / secondsDenominator)).ToString()
                );
        }

        /// <summary>
        /// Read the date taken timestamp from the loaded image.
        /// </summary>
        /// <returns>Date taken timestamp.</returns>
        private DateTime ReadDateTakenTimestamp()
        {
            if (string.IsNullOrEmpty(this.picturePath))
            {
                return DateTime.MinValue;
            }

            Image picture = Image.FromFile(this.PicturePath);
            // get the property at the id 36867
            PropertyItem propertyItem = picture.GetPropertyItem(0x9003);
            picture.Dispose();

            // convert the proptery into datetime object.
            string timeString = Encoding.UTF8.GetString(propertyItem.Value).Trim();
            int spaceIndex = timeString.IndexOf(" ");
            string secondPart = timeString.Substring(spaceIndex, (timeString.Length - spaceIndex));
            string firstPart = timeString.Substring(0, 10).Replace(":", "-");
            
            return DateTime.Parse(string.Concat(firstPart, secondPart));
        }

        /// <summary>
        /// Updates the filename of the picture.
        /// </summary>
        private void UpdateFilename()
        {
            if (string.IsNullOrEmpty(this.PicturePath))
            {
                return;
            }

            System.IO.FileInfo info = new System.IO.FileInfo(this.PicturePath);
            this.Filename = info.Name;
        }
    }
}
